#ifndef __CONFIG_H__
#define __CONFIG_H__

#include <memory>
#include <string>
#include <vector>
#include <list>
#include <yaml-cpp/yaml.h>
#include <boost/lexical_cast.hpp>
#include <unordered_map>

#include "log.h"
#include "singleton.h"

extern Logger::ptr logger;

class ConfigBase
{
public:
    typedef std::shared_ptr<ConfigBase> ptr;

    ConfigBase(const std::string& name = "", const std::string description = "")
        :m_description(description), m_name(name)
    {

    }

    virtual ~ConfigBase() {};

    const std::string& GetDescription() const
    {
        return m_description;
    }

    const std::string& GetName() const
    {
        return m_name;
    }

    virtual bool fromString(const std::string& val) = 0;

    virtual std::string toString() = 0;
private:
    std::string m_description;
    std::string m_name;
};

//从F转换到T
template<typename F, typename T>
class LexicalCast
{
public:
    T operator()(const F& f)
    {
        return boost::lexical_cast<T>(f);
    }
};

//LexicalCast的片特化，从std::string--->std::vector<T>
template<typename T>
class LexicalCast<std::string, std::vector<T> >
{
public:
    std::vector<T> operator()(const std::string& str)
    {
        YAML::Node node = YAML::Load(str);
        typename std::vector<T> vec;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); i++)
        {
            ss.str("");
            ss << node[i];
            vec.push_back(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }

};

//LexicalCast的片特化，从std::vector<T>--->std::string
template<class T>
class LexicalCast<std::vector<T>, std::string> 
{
public:
    std::string operator()(const std::vector<T>& vec)
    {
        YAML::Node node(YAML::NodeType::Sequence);
        for(auto& i : vec)
        {
            node.push_back(YAML::Node(LexicalCast<T, std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

//LexicalCast的片特化，从std::string--->std::list<T>
template<class T>
class LexicalCast<std::string, std::list<T> > 
{
public: 
    std::list<T> operator()(const std::string& str)
    {
        YAML::Node node = YAML::Load(str);
        typename std::list<T> lt;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); i++)
        {
            ss.str("");
            ss << node[i];
            lt.push_back(LexicalCast<T, std::string>()(ss.str()));
        }
        return lt;
    }
};

//LexicalCast的片特化，从std::list<T>--->std::string
template<class T>
class LexicalCast<std::list<T>, std::string>
{
public:
    std::string operator()(const std::list<T>& lt)
    {
        YAML::Node node(YAML::NodeType::Sequence);
        for(auto& i : lt)
        {
            node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

template<class T, class FromString = LexicalCast<std::string, T>, class ToString = LexicalCast<T, std::string> >
class Config : public ConfigBase
{
public:
    typedef std::shared_ptr<Config> ptr;

    void SetValue(T val)
    {
        m_value = val;
    }

    Config(const std::string name, const T value, const std::string description)
        :ConfigBase(name, description), m_value(value)
    {

    }

    bool fromString(const std::string& val) override
    {
        try
        {
            m_value = FromString()(val);
            return true;
        }
        catch(const std::exception& e)
        {
            LOG_ERROR(logger) << e.what();
        }
        return false;
    }

    std::string toString() override
    {
        std::string str = ToString()(m_value);
        return str;
    }

    const T& GetValue()
    {
        return m_value;
    }
private:
    T m_value;
};

class ConfigManager
{
public:
    typedef std::unordered_map<std::string, ConfigBase::ptr> ConfigMap;

    void LoadFromDir(const std::string& path);

    void LoadFromYaml(const YAML::Node& node);

    void Init();

    ConfigMap& GetConfigMap()
    {
        return m_configs;
    }

    template<class T>
    typename Config<T>::ptr GetConfig(const std::string& name)
    {
        auto it = m_configs.find(name);
        if(it != m_configs.end())
        {
            return std::dynamic_pointer_cast<Config<T>>(m_configs[name]);
        }
        return nullptr;
    }

    template<class T>
    typename Config<T>::ptr AddConfig(const std::string& name, const T& value, const std::string& description)
    {
        typename Config<T>::ptr conf = GetConfig<T>(name);
        if(conf != nullptr)
        {
            conf->SetValue(value);
        }
        else
        {
            conf = std::make_shared<Config<T> >(name, value, description);
            m_configs[name] = conf;
        }
        return conf;
    }

private:
    ConfigMap m_configs;
};

typedef Singleton<ConfigManager> ConfigMgr;

#endif // !__CONFIG_H__
